package com.syqc.comlib.rx

import androidx.annotation.NonNull
import androidx.annotation.Nullable
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.syqc.comlib.rx.LiveDataBus.ObserverWrapper
import java.lang.reflect.Field
import java.lang.reflect.Method

/**
 * @Author Jasper
 * @Time 2020/7/30
 * @Desc @[LiveDataBus]
 */
class LiveDataBus private constructor() {

    private val bus: MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap() }

    companion object {
        private val instance: LiveDataBus by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            LiveDataBus()
        }

        fun get(): LiveDataBus {
            return instance
        }
    }


    fun <T> with(key: String, type: Class<T>): BusMutableLiveData<T> {
        if (!bus.containsKey(key)) {
            bus[key] = BusMutableLiveData()
        }
        return bus[key] as BusMutableLiveData<T>
    }

    fun with(key: String): BusMutableLiveData<Any> {
        return with(key, Any::class.java)
    }

    private class ObserverWrapper<T>(private val observer: Observer<T>) : Observer<T> {
        override fun onChanged(@Nullable t: T) {
            if (isCallOnObserve()) {
                return
            }
            observer.onChanged(t)
        }

        private fun isCallOnObserve(): Boolean {
            val stackTrace = Thread.currentThread().stackTrace
            if (stackTrace.isNotEmpty()) {
                for (element in stackTrace) {
                    if ("android.arch.lifecycle.LiveData" == element.className
                        && "observeForever" == element.methodName
                    ) {
                        return true
                    }
                }
            }
            return false
        }


    }

    class BusMutableLiveData<T> : MutableLiveData<T>() {
        private val observerMap: MutableMap<Observer<in T>, Observer<in T>> = HashMap()
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(owner, observer)
            try {
                hook(observer)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        override fun observeForever(observer: Observer<in T>) {
            if (!observerMap.containsKey(observer)) {
                observerMap[observer] = ObserverWrapper(observer)
            }
            observerMap[observer]?.let {
                super.observeForever(it)
            }
        }


        override fun removeObserver(observer: Observer<in T>) {
            val realObserver: Observer<in T>? = if (observerMap.containsKey(observer)) {
                observerMap.remove(observer)
            } else {
                observer
            }
            realObserver?.let {
                super.removeObserver(it)
            }
        }


        @Throws(Exception::class)
        private fun hook(observer: Observer<in T>) {
            //get wrapper's version
            val classLiveData: Class<LiveData<*>> = LiveData::class.java
            val fieldObservers: Field = classLiveData.getDeclaredField("mObservers")
            fieldObservers.isAccessible = true
            val objectObservers: Any = fieldObservers.get(this)
            val classObservers: Class<*> = objectObservers.javaClass
            val methodGet: Method = classObservers.getDeclaredMethod("get", Any::class.java)
            methodGet.isAccessible = true
            val objectWrapperEntry: Any = methodGet.invoke(objectObservers, observer)
            var objectWrapper: Any? = null
            if (objectWrapperEntry is Map.Entry<*, *>) {
                objectWrapper = objectWrapperEntry.value
            }
            if (objectWrapper == null) {
                throw NullPointerException("Wrapper can not be bull!")
            }
            val classObserverWrapper: Class<*>? = objectWrapper.javaClass.superclass
            val fieldLastVersion: Field = classObserverWrapper!!.getDeclaredField("mLastVersion")
            fieldLastVersion.isAccessible = true
            //get livedata's version
            val fieldVersion: Field = classLiveData.getDeclaredField("mVersion")
            fieldVersion.isAccessible = true
            val objectVersion: Any = fieldVersion.get(this)
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion)
        }

        /**
         * 粘性消息
         * @param owner
         * @param observer
         */
        fun observeStick(owner: LifecycleOwner, observer: Observer<T>) {
            super.observe(owner, observer)
        }

    }

    fun remove(key: String) {
        bus.remove(key)
    }

    fun removeAll() {
        bus.clear()
    }
}